/*
 * Adpt.cpp
 *
 */

#include "Adpt.h"
#include "../logger/logger.h"
#include "../common/Exception.h"

namespace application {

Adpt::Adpt()
{
	m_Estantes.resize(ESPACIO_ALMACEN);

	int i;

	for(i=0; i <ESPACIO_ALMACEN; i++)
		m_EstantesLibresNums.push_back(i);
}

Adpt::~Adpt()
{
}

bool Adpt::reservarEstantesParaProducto(ProductoCantidad &tipoProducto, EstanteNumList &estantes)
{
	bool retVal= false;

	if (tipoProducto.cantidad < 0)
		throw Exception("La cantidad de productos debe ser positiva");

	if(m_EstantesLibresNums.size() > tipoProducto.cantidad)
	{
		int cantidadRestante;
		int estanteId;

		for(cantidadRestante = tipoProducto.cantidad;
			cantidadRestante > 0; cantidadRestante--)
		{
			estanteId = m_EstantesLibresNums.front();
			m_EstantesLibresNums.pop_front();

			m_Estantes[estanteId].producto.tipo=tipoProducto.tipo;
			m_Estantes[estanteId].status=EN_PRODUCCION;

			estantes.push_back(estanteId);
		}

		retVal = true;
	}

	return retVal;
}

void Adpt::retirarProductoEstante(int estanteId,
									  Producto &producto)
{
	if(estanteId > m_Estantes.size())
		throw Exception("El id de estante no existe");

	producto = m_Estantes[estanteId].producto;

	m_Estantes[estanteId].status = ESTANTE_VACIO;
	m_Estantes[estanteId].ordenCompra.ordenCompraId = UNDEFINED_ID_COMPRA;
	m_Estantes[estanteId].producto.productoId = UNDEFINED_ID_PRODUCTO;
	m_EstantesLibresNums.push_back(estanteId);
}

std::string Adpt::serialize()
{
	std::string retStr;
	application::Adpt::EstanteCollection::iterator estanteListIt;

	//Voy recorriendo la lista de estantes y reservo los
	for(estanteListIt = m_Estantes.begin();
		estanteListIt != m_Estantes.end();
		estanteListIt++)
	{
		retStr += estanteListIt->serialize();
	}

//	OrdenCompraTipoProductoMap::iterator ocTipoIt;
//	//Voy recorriendo la lista de estantes y reservo los
//	for(ocTipoIt = m_ordenesCompraCantidades.begin();
//		ocTipoIt != m_ordenesCompraCantidades.end();
//		ocTipoIt++)
//	{
//		std::cout << "OC:"<< ocTipoIt->ordenCompra.ordenCompraId
//				  << " producto:"<<ocTipoIt->producto.tipo
//				  << " cantidad:"<<ocTipoIt->producto.cantidad <<std::endl;
//
//		std::cout << "size:"<<m_ordenesCompraCantidades.size()<<std::endl;
//	}

	return retStr;
}


int Adpt::obtenerCantEstantesVacios()
{
	return m_EstantesLibresNums.size();
}

int Adpt::obtenerEstanteVacio()
{
	int estanteId;

	if (m_EstantesLibresNums.size() <= 0)
		throw Exception("No hay espacio libre para almacenar el producto");

	estanteId = m_EstantesLibresNums.front();
	m_EstantesLibresNums.pop_front();

	return estanteId;
}

int Adpt::obtenerEstanteProducto(TipoProducto tipo)
{
	int estanteId = -1;
	int i;
	EstanteCollection::iterator it;

	for(it = m_Estantes.begin(), i=0;it != m_Estantes.end();it++,i++)
	{
		if(it->producto.tipo == tipo && it->status!= ESTANTE_LLENO)
		{
			if(it->ordenCompra.ordenCompraId != UNDEFINED_ID_COMPRA)
				return i;
			else
				estanteId = i;
		}
	}

	if(estanteId < 0)
		throw Exception("No se encontro reservado para el producto");

	return estanteId;
}

void Adpt::asignarOCEstantes(EstanteNumList &estanteList,
								const OrdenCompraHeader ordenCompra)
{
	application::Adpt::EstanteNumList::iterator estanteListIt;
	OrdenCompraTipoProducto ocTipoProd;

	//Voy recorriendo la lista de estantes y reservo los
	for(estanteListIt = estanteList.begin();
		estanteListIt != estanteList.end();
		estanteListIt++)
	{
		//Si el producto no existe todavia agrego a la lista de pendientes
		if(m_Estantes[*estanteListIt].status != ESTANTE_LLENO)
		{
			ocTipoProd.ordenCompra = ordenCompra;
			ocTipoProd.producto.tipo = m_Estantes[*estanteListIt].producto.tipo;

			modifyOCTipoProd(ocTipoProd,1);
		}

		m_Estantes[*estanteListIt].ordenCompra =ordenCompra;
	}
}

void Adpt::almacenarProducto(int estanteId, Producto &producto)
{
	OrdenCompraTipoProducto ocTipoProd;
	m_Estantes[estanteId].producto = producto;
	m_Estantes[estanteId].status = ESTANTE_LLENO;

	if(m_Estantes[estanteId].ordenCompra.ordenCompraId != UNDEFINED_ID_COMPRA)
	{
		ocTipoProd.ordenCompra = m_Estantes[estanteId].ordenCompra;
		ocTipoProd.producto.tipo = producto.tipo;

		modifyOCTipoProd(ocTipoProd,-1);

		if(getOCTipoProd(ocTipoProd)< 0)
			throw Exception("La cantidad de prods para una orden de compra se hizo <0");
	}
}

Adpt::EstanteNumList Adpt::buscarProductoSinOC(TipoProducto producto)
{
	return buscarProductoConOC(UNDEFINED_ID_COMPRA,producto);
}

int Adpt::getCantidadProductosRestantesOC(const TipoProducto producto,
		const OrdenCompraHeader ordenCompra)
{
	OrdenCompraTipoProducto ocTipoProd;

	ocTipoProd.ordenCompra = ordenCompra;
	ocTipoProd.producto.tipo = producto;

	return getOCTipoProd(ocTipoProd);
}

void Adpt::getOrdenCompraEstante(int estanteId,OrdenCompraHeader &ordenCompra) const
{
	ordenCompra = m_Estantes[estanteId].ordenCompra;
}

void Adpt::getProductoCantidad(OrdenCompraHeader &ordenCompra,ProductoCantidad& prodCantidad)
{
	prodCantidad = m_ordenesCompraProductos[ordenCompra];
}

Adpt::EstanteNumList Adpt::buscarProductoConOC(OrdenCompraId ordenCompraId,
		TipoProducto tipoProducto)
{
	int i;
	int cantidadEstantesAlmacen;
	Adpt::EstanteNumList estantesProducto;

	cantidadEstantesAlmacen = m_Estantes.size();

	for(i=0;i <cantidadEstantesAlmacen;i++)
	{
		if(m_Estantes[i].producto.tipo == tipoProducto &&
		   m_Estantes[i].ordenCompra.ordenCompraId == ordenCompraId &&
		   m_Estantes[i].status != ESTANTE_VACIO)
			estantesProducto.push_back(i);
	}

	return estantesProducto;
}

void Adpt::modifyOCTipoProd(OrdenCompraTipoProducto ocTipoProd,int cantidad)
{
	OrdenCompraTipoProductoMap::iterator ocTipoIt;

	//Voy recorriendo la lista de estantes y reservo los
	for(ocTipoIt = m_ordenesCompraCantidades.begin();
		ocTipoIt != m_ordenesCompraCantidades.end();
		ocTipoIt++)
	{
		if(ocTipoIt->ordenCompra.ordenCompraId == ocTipoProd.ordenCompra.ordenCompraId
				&& ocTipoIt->producto.tipo == ocTipoProd.producto.tipo)
		{
			ocTipoIt->producto.cantidad+=cantidad;
			break;
		}
	}

	if(ocTipoIt == m_ordenesCompraCantidades.end())
	{
		m_ordenesCompraCantidades.push_back(ocTipoProd);
		m_ordenesCompraCantidades.back().producto.cantidad=cantidad;
	}

}

int Adpt::getOCTipoProd(OrdenCompraTipoProducto ocTipoProd)
{
	OrdenCompraTipoProductoMap::iterator ocTipoIt;

	//Voy recorriendo la lista de estantes y reservo los
	for(ocTipoIt = m_ordenesCompraCantidades.begin();
		ocTipoIt != m_ordenesCompraCantidades.end();
		ocTipoIt++)
	{
		if(ocTipoIt->ordenCompra.ordenCompraId == ocTipoProd.ordenCompra.ordenCompraId
				&& ocTipoIt->producto.tipo == ocTipoProd.producto.tipo)
		{
			return ocTipoIt->producto.cantidad;
		}
	}

	if(ocTipoIt == m_ordenesCompraCantidades.end())
	{
		throw Exception("Orden compra cantidades not found");
	}
	return -1;
}

} /* namespace application */
